﻿using System;
using System.Data.Linq;
using System.Linq;
using System.Linq.Expressions;
using Magiq.Providers.Sql.Support;

namespace Magiq.Providers.Sql.Query
{
    public static class CompiledQueryExtensions
    {
        public static IQueryable<TResult> CompiledQuery<T, TResult>
            (this IQueryable<T> self, string key, Expression<Func<IQueryable<T>, IQueryable<TResult>>> expression) where T : class
        {
            return self.Dispatch(
                 () => TableCompiledQueryableProvider<T, TResult>.GetQueryable((Table<T>)self, key, expression),
                 () => EntitySetEntitySetCompiledQueryableProvider<T, TResult>.GetQueryable(((IEntitySetAdapterProvider)self).EntitySetAdapter, key, expression)
            );
        }

        public static IQueryable<TResult> CompiledQuery<T, TArgs0, TResult>
            (this IQueryable<T> self, string key, Expression<Func<IQueryable<T>, TArgs0, IQueryable<TResult>>> expression, TArgs0 arg0) where T : class
        {
            return self.Dispatch(
                () => TableCompiledQueryableProvider<T, TArgs0, TResult>.GetQueryable((Table<T>)self, key, expression, arg0),
                () => EntitySetEntitySetCompiledQueryableProvider<T, TArgs0, TResult>.GetQueryable(((IEntitySetAdapterProvider)self).EntitySetAdapter, key, expression, arg0)
            );
        }

        public static IQueryable<TResult> CompiledQuery<T, TArgs0, TArgs1, TResult>
            (this IQueryable<T> self, string key, Expression<Func<IQueryable<T>, TArgs0, TArgs1, IQueryable<TResult>>> expression, TArgs0 arg0, TArgs1 arg1)
            where T : class
        {
            return self.Dispatch(
                () => TableCompiledQueryableProvider<T, TArgs0, TArgs1, TResult>.GetQueryable((Table<T>)self, key, expression, arg0, arg1),
                () => EntitySetEntitySetCompiledQueryableProvider<T, TArgs0, TArgs1, TResult>.GetQueryable(((IEntitySetAdapterProvider)self).EntitySetAdapter, key, expression, arg0, arg1)
            );
        }
        private static IQueryable<TResult> Dispatch<TResult>(this IQueryable queryable, Func<IQueryable<TResult>> repository, Func<IQueryable<TResult>> collection) 
        {
            if (queryable is ITable)
                return repository();

            if (queryable is IEntitySetAdapterProvider)
                return collection();

            throw new InvalidOperationException(
                string.Format("Query {0} is not supported as a provider for Linq-to-Sql compiled queries.",
                              queryable.GetType().FullName));
        }
    }
}